package com.engine4cloud.tdf.supervision.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.engine4cloud.tdf.admin.api.entity.SysDept;
import com.engine4cloud.tdf.admin.api.feign.RemoteDeptService;
import com.engine4cloud.tdf.common.core.constant.SecurityConstants;
import com.engine4cloud.tdf.common.core.constant.SupervisionBusinessConstants;
import com.engine4cloud.tdf.common.core.constant.enums.CorAuditActionStatusEnum;
import com.engine4cloud.tdf.common.core.constant.enums.CorFileTypeEnum;
import com.engine4cloud.tdf.common.core.constant.enums.DictNameEnum;
import com.engine4cloud.tdf.common.core.exception.ValidateCodeException;
import com.engine4cloud.tdf.common.core.util.CodeUtils;
import com.engine4cloud.tdf.common.core.util.R;
import com.engine4cloud.tdf.common.core.util.RetOps;
import com.engine4cloud.tdf.common.core.util.lambda.Actions;
import com.engine4cloud.tdf.common.data.web.query.Pages;
import com.engine4cloud.tdf.common.data.web.query.Queries;
import com.engine4cloud.tdf.common.data.web.response.PageInfo;
import com.engine4cloud.tdf.common.data.web.response.Params;
import com.engine4cloud.tdf.common.security.service.TdfUser;
import com.engine4cloud.tdf.common.security.util.SecurityUtils;
import com.engine4cloud.tdf.supervision.convert.JzdxConvert;
import com.engine4cloud.tdf.supervision.convert.JzdxCrimeInfoConvert;
import com.engine4cloud.tdf.supervision.convert.JzdxDepartConvert;
import com.engine4cloud.tdf.supervision.dto.query.*;
import com.engine4cloud.tdf.supervision.dto.result.*;
import com.engine4cloud.tdf.supervision.dto.IdentityUniqueCheckDTO;
import com.engine4cloud.tdf.supervision.dto.JzdxAuditDTO;
import com.engine4cloud.tdf.supervision.dto.create.JzdxAuditCreateDTO;
import com.engine4cloud.tdf.supervision.dto.create.JzdxCreateDTO;
import com.engine4cloud.tdf.supervision.dto.create.JzdxDepartCreateDTO;
import com.engine4cloud.tdf.supervision.dto.update.JzdxAuditDepartUpdateDTO;
import com.engine4cloud.tdf.supervision.dto.update.JzdxDepartUpdateDTO;
import com.engine4cloud.tdf.supervision.dto.update.JzdxPreActionUpdateDTO;
import com.engine4cloud.tdf.supervision.dto.update.JzdxUpdateDTO;
import com.engine4cloud.tdf.supervision.entity.JzdxEntity;
import com.engine4cloud.tdf.supervision.mapper.JzdxMapper;
import com.engine4cloud.tdf.supervision.service.*;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 社区矫正人员基本信息采集表
 *
 * @author wenjie
 * @date 2024-03-14 11:20:26
 */
@Service
public class JzdxServiceImpl extends ServiceImpl<JzdxMapper, JzdxEntity> implements JzdxService {

    @Resource
    private JzdxDepartService departService;

    @Resource
    private JzdxIdentityService identityService;

    @Resource
    private JzdxPhysicalConditionService physicalConditionService;

    @Resource
    private JzdxEmploymentService employmentService;

    @Resource
    private JzdxCrimeInfoService crimeInfoService;

    @Resource
    private JzdxAuditResultService auditResultService;

    @Resource
    private DictSelectService dictSelectService;

    @Resource
    private JzdxFileService fileService;

    @Resource
    private RemoteDeptService remoteDeptService;

    @Override
    public PageInfo<JzdxResultDTO> pageList(Params pageable, JzdxQueryDTO dto) {
        R<List<Long>> deptIdRemoteList = remoteDeptService.getDescendantIdList(dto.getDeptId(), SecurityConstants.FROM_IN);

        List<Long> deptIdList = RetOps.of(deptIdRemoteList).getData().filter(CollectionUtils::isNotEmpty)
                .orElseThrow(() -> new ValidateCodeException("没有数据权限!"));

        IPage<JzdxResultDTO> page = this.baseMapper.pageList(Pages.page(pageable), dto, deptIdList);
        page.setRecords(this.preCrimeInfoList(page.getRecords()));
        return Pages.convert(page);
    }

    @Override
    public JzdxResultDTO getById(Long id) {
        JzdxResultDTO result = Optional.ofNullable(this.baseMapper.detail(id))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));
        //设置犯罪信息
        this.setCrimeInfoList(result);
        //设置国籍信息
        this.setNativePlaceList(result);
        //设置居住地户籍地信息
        JzdxConvert.INSTANCE.convertList(result);
        //设置证件信息
        result.setIdentity(identityService.find(result.getRybh()));
        //设置身体状况
        result.setPhysicalCondition(physicalConditionService.find(result.getRybh()));
        //设置就业就学信息
        result.setEmployment(employmentService.find(result.getRybh()));
        //设置照片信息
        this.setPhotoList(result);
        return result;
    }

    @Override
    public JzdxResultDTO getByCode(String code) {
        JzdxEntity entity = Optional.ofNullable(this.getOne(wrapper(new JzdxQueryDTO(code))))
                .orElseThrow(() -> new ValidateCodeException("矫正对象数据不存在"));

        return JzdxConvert.INSTANCE.convert(entity);
    }

    @Override
    public JzdxEntity getByIdentityNotRelease(String identity) {
        LambdaQueryWrapper<JzdxEntity> wrapper = Queries.lambda();
        Queries.accept(identity, v -> wrapper.eq(JzdxEntity::getZjh, v));
        wrapper.ne(JzdxEntity::getPreAction, CorAuditActionStatusEnum.RELEASE);
        return this.getOne(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String create(JzdxCreateDTO dto) {
        Boolean unique = this.checkUniqueByIdentity(new IdentityUniqueCheckDTO(dto.getZjh()));
        if (!unique) {
            throw new ValidateCodeException("证件号已存在");
        }

        TdfUser user = SecurityUtils.getUser();
        //设置矫正对象主要信息
        JzdxEntity entity = JzdxConvert.INSTANCE.create(dto, user.getTenantId(), user.getDeptId(), CorAuditActionStatusEnum.CREATE);
        //设置人员编号
        entity.setRybh(this.initCorrectorCode(user.getDeptId()));
        //保存矫正对象信息
        super.save(entity);
        //保存证件信息
        Actions.apply(Objects.nonNull(dto.getIdentity()), () -> identityService.create(dto.getIdentity(), entity.getRybh()));
        //保存身体状况
        Actions.apply(Objects.nonNull(dto.getPhysicalCondition()), () -> physicalConditionService.create(dto.getPhysicalCondition(), entity.getRybh()));
        //保存就业就学信息
        Actions.apply(Objects.nonNull(dto.getEmployment()), () -> employmentService.create(dto.getEmployment(), entity.getRybh()));
        //保存照片
        Actions.apply(CollectionUtils.isNotEmpty(dto.getPhotoList()), () -> fileService.batchSaveFile(new JzdxFileQueryDTO(entity.getRybh(), CorFileTypeEnum.CORRECTOR_PHOTOGRAPH), dto.getPhotoList(), user));
        //保存具体罪名和犯罪类型
        Actions.apply(Objects.nonNull(dto.getCrimeInfoList()), () -> crimeInfoService.saveBatchCrimeInfoList(JzdxCrimeInfoConvert.INSTANCE.create(entity, dto.getCrimeInfoList())));
        //设置状态
        auditResultService.create(new JzdxAuditCreateDTO(entity.getRybh()), new JzdxAuditDTO(user.getDeptId(), CorAuditActionStatusEnum.CREATE));
        return entity.getRybh();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(JzdxUpdateDTO dto) {
        JzdxEntity old = Optional.ofNullable(baseMapper.selectById(dto.getId()))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));

        Boolean unique = this.checkUniqueByIdentity(new IdentityUniqueCheckDTO(dto.getId(), dto.getZjh()));
        if (!unique) {
            throw new ValidateCodeException("证件号已存在");
        }

        TdfUser user = SecurityUtils.getUser();
        //保存矫正对象信息
        super.updateById(JzdxConvert.INSTANCE.update(dto));
        //更新证件信息
        identityService.updateByCode(dto.getIdentity(), old.getRybh());
        //更新身体状况
        physicalConditionService.updateByCode(dto.getPhysicalCondition(), old.getRybh());
        //更新就业就学信息
        employmentService.updateByCode(dto.getEmployment(), old.getRybh());
        //更新照片
        fileService.batchUpdateFile(new JzdxFileQueryDTO(old.getRybh(), CorFileTypeEnum.CORRECTOR_PHOTOGRAPH), dto.getPhotoList(), user);
        //更新具体罪名和犯罪类型
        crimeInfoService.updateBatch(old, dto.getCrimeInfoList());
        return true;
    }

    @Override
    public Boolean checkUniqueByIdentity(IdentityUniqueCheckDTO dto) {
        LambdaQueryWrapper<JzdxEntity> wrapper = Queries.lambda();
        Queries.accept(dto.getIdentity(), v -> wrapper.eq(JzdxEntity::getZjh, v));
        wrapper.ne(JzdxEntity::getPreAction, CorAuditActionStatusEnum.RELEASE);

        Optional<JzdxEntity> optional = Optional.ofNullable(this.getOne(wrapper));
        return optional.map(dx -> Objects.equals(dx.getId(), dto.getId())).orElse(true);
    }

    @Override
    public Boolean changeDepart(JzdxAuditDepartUpdateDTO dto, CorAuditActionStatusEnum actionStatus) {
        JzdxEntity entity = Optional.ofNullable(this.getOne(wrapper(new JzdxQueryDTO(dto.getRybh()))))
                .orElseThrow(() -> new ValidateCodeException("数据不存在"));

        entity.setDeptId(dto.getDeptId());
        baseMapper.updateById(entity);
        return true;
    }

    @Override
    public Boolean setDepart(JzdxResultDTO dx) {
        R<SysDept> villageDept = remoteDeptService.detail(dx.getDeptId(), SecurityConstants.FROM_IN);
        SysDept village = RetOps.of(villageDept)
                .getData()
                .orElseThrow(() -> new ValidateCodeException("所属司法所不存在"));

        SysDept county = null;
        if (Objects.nonNull(village)) {
            R<SysDept> countyDept = remoteDeptService.detail(village.getParentId(), SecurityConstants.FROM_IN);
            county = RetOps.of(countyDept)
                    .getData().orElse(null);
        }

        SysDept city = null;
        if (Objects.nonNull(county)) {
            R<SysDept> cityDept = remoteDeptService.detail(county.getParentId(), SecurityConstants.FROM_IN);
            city = RetOps.of(cityDept)
                    .getData().orElse(null);
        }

        SysDept province = null;
        if (Objects.nonNull(city)) {
            R<SysDept> provinceDept = remoteDeptService.detail(city.getParentId(), SecurityConstants.FROM_IN);
            province = RetOps.of(provinceDept)
                    .getData().orElse(null);
        }

        JzdxDepartResultDTO old = departService.findByCode(dx.getRybh());

        if (Objects.isNull(old)) {
            return departService.create(new JzdxDepartCreateDTO(dx.getRybh(),
                    JzdxDepartConvert.INSTANCE.convertToProvince(province),
                    JzdxDepartConvert.INSTANCE.convertToCity(city),
                    JzdxDepartConvert.INSTANCE.convertToCounty(county),
                    JzdxDepartConvert.INSTANCE.convertToVillage(village)));
        }

        return departService.update(new JzdxDepartUpdateDTO(dx.getRybh(),
                JzdxDepartConvert.INSTANCE.convertToProvince(province),
                JzdxDepartConvert.INSTANCE.convertToCity(city),
                JzdxDepartConvert.INSTANCE.convertToCounty(county),
                JzdxDepartConvert.INSTANCE.convertToVillage(village)));

    }

    private void setNativePlaceList(JzdxResultDTO result) {
        List<DictSelectResultDTO> countryList = dictSelectService.items(DictNameEnum.COUNTRY.getType());
        if (CollectionUtils.isEmpty(countryList)) {
            return;
        }

        List<String> nativePlaceList = new ArrayList<>();
        Map<String, DictSelectResultDTO> countryMap = countryList.stream().collect(Collectors.toMap(DictSelectResultDTO::getCode, v -> v));

        String nativePlace = result.getGj();
        while (!Objects.equals(SupervisionBusinessConstants.AREA_ROOT_CODE, nativePlace) && countryMap.containsKey(nativePlace)) {
            nativePlaceList.add(nativePlace);
            DictSelectResultDTO currentPlace = countryMap.get(nativePlace);
            nativePlace = currentPlace.getPcode();
        }

        ///翻转，父在前，子在后
        Collections.reverse(nativePlaceList);
        result.setNativePlaceList(nativePlaceList);
    }


    /**
     * 获取当前部门下最大的矫正编号
     *
     * @param deptId 部门id
     * @return JzdxCodeGenDTO
     */
    @Override
    public String initCorrectorCode(Long deptId) {
        R<SysDept> result = remoteDeptService.detail(deptId, SecurityConstants.FROM_IN);
        SysDept dept = RetOps.of(result)
                .getData()
                .orElseThrow(() -> new ValidateCodeException("部门编码不存在"));

        String prefix = CodeUtils.entrustCorrectorCodePrefix(dept.getOrgCode());
        String deptMaxCode = this.baseMapper.findDeptMaxCode(prefix);

        return CodeUtils.entrustCorrectorCode(prefix, deptMaxCode);
    }

    @Override
    public Integer absence(String code) {
        return this.baseMapper.absence(code);
    }

    @Override
    public void changeAction(JzdxPreActionUpdateDTO dto) {
        JzdxEntity entity = Optional.ofNullable(this.getOne(wrapper(new JzdxQueryDTO(dto.getRybh()))))
                .orElseThrow(() -> new ValidateCodeException("矫正对象数据不存在"));

        entity.setPreAction(dto.getAction());
        super.updateById(entity);
    }

    @Override
    public PageInfo<JzdxSimpleResultDTO> simplePageList(JzdxSimpleQueryDTO dto) {
        R<List<Long>> deptIdRemoteList = remoteDeptService.getDescendantIdList(dto.getDeptId(), SecurityConstants.FROM_IN);

        List<Long> deptIdList = RetOps.of(deptIdRemoteList).getData().filter(CollectionUtils::isNotEmpty)
                .orElseThrow(() -> new ValidateCodeException("没有数据权限!"));

        IPage<JzdxSimpleResultDTO> page = this.baseMapper.simplePageList(Pages.page(dto), dto, deptIdList);
        return Pages.convert(page);
    }

    /**
     * 设置罪名
     *
     * @param dto dto
     */
    private void setCrimeInfoList(JzdxResultDTO dto) {
        List<JzdxCrimeInfoResultDTO> crimeInfoList = crimeInfoService.findList(new JzdxCrimeInfoQueryDTO(dto.getRybh()));
        if (CollectionUtils.isNotEmpty(crimeInfoList)) {
            List<String> categoryList = crimeInfoList.stream().map(JzdxCrimeInfoResultDTO::getFzlx).toList();
            List<DictSelectResultDTO> crimeNameList = dictSelectService.items(DictNameEnum.CRIMINAL_NAME.getType());
            List<DictSelectResultDTO> crimeTypeList = dictSelectService.items(new DictCodeListQueryDTO(categoryList, DictNameEnum.CRIMINAL_TYPE.getType()));
            //设置犯罪类型名称
            JzdxCrimeInfoConvert.INSTANCE.convertCrimeInfoList(crimeInfoList, crimeNameList, crimeTypeList);
            dto.setCrimeInfoList(crimeInfoList);
        }
    }

    /**
     * 设置犯罪类型名称
     *
     * @param recordList recordList
     * @return List<JzdxResultDTO>
     */
    private List<JzdxResultDTO> preCrimeInfoList(List<JzdxResultDTO> recordList) {
        if (CollectionUtils.isEmpty(recordList)) {
            return recordList;
        }
        List<String> codeList = recordList.stream().map(JzdxResultDTO::getRybh).toList();
        List<JzdxCrimeInfoResultDTO> crimeInfoList = crimeInfoService.findList(new JzdxCrimeInfoQueryDTO(codeList));
        List<String> categoryList = crimeInfoList.stream().map(JzdxCrimeInfoResultDTO::getFzlx).toList();
        List<DictSelectResultDTO> crimeNameList = dictSelectService.items(DictNameEnum.CRIMINAL_NAME.getType());
        List<DictSelectResultDTO> crimeTypeList = dictSelectService.items(new DictCodeListQueryDTO(categoryList, DictNameEnum.CRIMINAL_TYPE.getType()));
        return recordList.stream().map(record -> JzdxCrimeInfoConvert.INSTANCE.convertCrimeInfoList(record, crimeInfoList, crimeNameList, crimeTypeList)).toList();
    }

    private void setPhotoList(JzdxResultDTO dto) {
        List<JzdxFileResultDTO> photoList = fileService.findList(new JzdxFileQueryDTO(dto.getRybh(), CorFileTypeEnum.CORRECTOR_PHOTOGRAPH));
        dto.setPhotoList(photoList);
    }

    private LambdaQueryWrapper<JzdxEntity> wrapper(JzdxQueryDTO query) {
        LambdaQueryWrapper<JzdxEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(JzdxEntity::getId, v));
        Queries.accept(query.getRybh(), v -> wrapper.eq(JzdxEntity::getRybh, v));
        Queries.accept(query.getJzlb(), v -> wrapper.eq(JzdxEntity::getJzlb, v));
        Queries.accept(query.getXm(), v -> wrapper.like(JzdxEntity::getXm, v));
        return wrapper;
    }

}